
package geneontofx.go;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


public class OntologyModel implements IOntology {
    
    private static OntologyModel ontologyInstance = null;
    
    public static String DEF_PATH = "src\\_data\\gene_ontology_ext.obo";
    private String filePath;
    private boolean parsed;
    private ConcurrentHashMap<String, OntologyTerm> ontologyTerms;
    
    private OntologyModel() {
        ontologyTerms = new ConcurrentHashMap<>();
    }
    
    public static OntologyModel getOntologyInstance() {
        if(ontologyInstance == null) {
            ontologyInstance = new OntologyModel();
        }
        return ontologyInstance;
    }
    
    @Override
    public String getOntologyPath() {
        if(this.filePath == null) {
            filePath = DEF_PATH;
        }
        return filePath;
    }

    @Override
    public void setOntologyPath(String path) {
        this.filePath = path;
    }

    @Override
    public ConcurrentHashMap<String, OntologyTerm> getOntologyTerms() {
        return ontologyTerms;
    }

    @Override
    public void parseOntology(String path) {
        path = getOntologyPath();
        try {
            BufferedReader input;
            input = new BufferedReader(new FileReader(path));

            String line, prefix, relationType;
            while((line = input.readLine()) != null) {
                if(line.equals("[Term]")) {
                    OntologyTerm currOntologyTerm = new OntologyTerm();
                    while(!"".equals(line = input.readLine())) {
                        prefix = line.substring(0, line.indexOf(":"));
                        switch(prefix) {
                            case "id":
                                currOntologyTerm.setId(line.substring(line.indexOf(":")+2));
                                break;
                            case "name":
                                currOntologyTerm.setName(line.substring(line.indexOf(":")+2));
                                break;
                            case "namespace":
                                currOntologyTerm.setNamespace(line.substring(line.indexOf(":")+2));
                                break;
                            case "def":
                                currOntologyTerm.setDefinition(line.substring(line.indexOf("\"")+1, line.lastIndexOf("\"")));
                                break;
                            case "is_a":
                                currOntologyTerm.addRelation(line.substring(line.indexOf(":")+2, line.indexOf("!")-1), OntologyTerm.Relations.ISA);
                                currOntologyTerm.setHasParents(true);
                                break;
                            case "relationship":
                                relationType = line.substring(line.indexOf(":")+2, line.indexOf(" GO:"));
                                if(relationType.toLowerCase().contains("regulates")) {
                                    currOntologyTerm.addRelation(line.substring(line.indexOf("GO:"), line.indexOf(" ! ")), OntologyTerm.Relations.REGULATES);
                                    currOntologyTerm.setHasParents(true);
                                }
                                if(relationType.toLowerCase().contains("part_of")) {
                                    currOntologyTerm.addRelation(line.substring(line.indexOf("GO:"), line.indexOf(" ! ")), OntologyTerm.Relations.PARTOF);
                                    currOntologyTerm.setHasParents(true);
                                }
                                if(relationType.toLowerCase().contains("occurs")) {
                                    currOntologyTerm.addRelation(line.substring(line.indexOf("GO:"), line.indexOf(" ! ")), OntologyTerm.Relations.OCCURSIN);
                                    currOntologyTerm.setHasParents(true);
                                }
                                if(relationType.toLowerCase().contains("has_part")) {
                                    currOntologyTerm.addRelation(line.substring(line.indexOf("GO:"), line.indexOf(" ! ")), OntologyTerm.Relations.HASPART);
                                    currOntologyTerm.setHasChildren(true);
                                }
                                break;
                            case "is_obsolete":
                                currOntologyTerm.setObsolete(true);
                                break;
                            case "TODO":
                                break;
                        }
                    }
                    currOntologyTerm.verifyComplete();
                    ontologyTerms.put(currOntologyTerm.getId(), currOntologyTerm);
                }
            }
            input.close();
        } catch (FileNotFoundException fileNotFoundEx){
            System.err.format("File Not Found: %s%n", fileNotFoundEx);
        } catch (IOException ioEx) {
            System.err.format("IO Exeption: %s%n", ioEx);
        }
    }

    @Override
    public void remakeRelations() {
        /*
         * check and update relations
         */
        for(Map.Entry entry : ontologyTerms.entrySet()) {
            String currKey = (String) entry.getKey();
            OntologyTerm currValue = (OntologyTerm) entry.getValue();
            updateAllRelations(currValue);
        }
        setParsed(true);
    }
    
    public void updateAllRelations(OntologyTerm currTerm) {
        if(currTerm.hasParents()) {
            ArrayList<ArrayList> parents = currTerm.getParents();
            for (Iterator<ArrayList> parentsIt = parents.iterator(); parentsIt.hasNext();) {
                @SuppressWarnings("unchecked") // cant cast it to ArrayList<OntologyTerm>
                ArrayList<OntologyTerm> parentList = (ArrayList<OntologyTerm>) parentsIt.next();
                if(parentList != null) {
                    for (ListIterator<OntologyTerm> relationIt = parentList.listIterator(); relationIt.hasNext();) {
                        OntologyTerm relation = relationIt.next();
                        OntologyTerm fullTerm = ontologyTerms.get(relation.getId());
                        relationIt.remove();
                        relationIt.add(fullTerm);
                    }
                }
            }
            currTerm.setComplete(true);
        }
        if(currTerm.hasChildren()) {
            ArrayList<ArrayList> children = currTerm.getChildren();
            for (Iterator<ArrayList> childrenIt = children.iterator(); childrenIt.hasNext();) {
                @SuppressWarnings("unchecked") // cant cast it to ArrayList<OntologyTerm>
                ArrayList<OntologyTerm> childrenList = childrenIt.next();
                if(childrenList != null) {
                    for (ListIterator<OntologyTerm> relationIt = childrenList.listIterator(); relationIt.hasNext();) {
                        OntologyTerm relation = relationIt.next();
                        OntologyTerm fullTerm = ontologyTerms.get(relation.getId());
                        relationIt.remove();
                        relationIt.add(fullTerm);
                        
                    }
                }
            }
        }
    }

    public boolean isParsed() {
        return parsed;
    }

    public void setParsed(boolean parsed) {
        this.parsed = parsed;
    }
}
